package com.timeriver.utils;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 类对象转换：实体类 <-> 数据传输对象 <-> 视图对象
 */
public class EntityConvertUtil {

    /**
     * 对单个对象的属性进行复制
     * @param object
     * @param clazz
     * @param <T>
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static <T> T object2DtoOrEntity(Object object, Class<T> clazz) {
        T t = null;
        try {
            t = clazz.newInstance();
            BeanUtils.copyProperties(object, t);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return t;
    }

    /**
     * 对一组对象的属性进行复制
     * @param objects
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> objects2DtosOrEntitys(List<?> objects, Class<T> clazz) {
        List<T> list = objects.stream().parallel()
                .map(elem -> object2DtoOrEntity(elem, clazz))
                .collect(Collectors.toList());
        return list;
    }

    /**
     * 更新相同对象的属性
     * @param object2Entity
     * @param entity 更新后的对象
     * @param <T>
     * @return
     */
    public static <T> T updateObject(T object2Entity, T entity) {
        Class<?> entityClass = entity.getClass();
        /** 获取待更新对象的属性名称 */
        Field[] fields = entityClass.getDeclaredFields();
        try {
            for (Field f : fields) {
                /** 获取对象的get方法 */
                String name = f.getName();
                name = name.substring(0, 1).toUpperCase() + name.substring(1);
                Method getMethod = object2Entity.getClass().getDeclaredMethod("get" + name);
                /** 根据数据类型进行属性更新 */
                String type = f.getGenericType().toString();
                switch (type) {
                    case "class java.lang.String":
                        String string = (String) getMethod.invoke(object2Entity);
                        if (StringUtils.isNotEmpty(string)) {
                            Method setMethod = entity.getClass()
                                    .getDeclaredMethod("set" + name, new Class[]{String.class});
                            setMethod.invoke(entity, new Object[] {string});
                        }
                        break;
                    case "class java.lang.Integer":
                        Object intObj = getMethod.invoke(object2Entity);
                        if (intObj != null) {
                            Integer intNum = (Integer) intObj;
                            Method setMethod = entity.getClass()
                                    .getDeclaredMethod("set" + name, new Class[]{Integer.class});
                            setMethod.invoke(entity, new Object[] {intNum});
                        }
                        break;
                    case "class java.lang.Double":
                        Object doubleObj = getMethod.invoke(object2Entity);
                        if (doubleObj != null) {
                            Double doubleNum = (Double) doubleObj;
                            Method setMethod = entity.getClass()
                                    .getDeclaredMethod("set" + name, new Class[]{Double.class});
                            setMethod.invoke(entity, new Object[] {doubleNum});
                        }
                        break;
                    case "class java.lang.Boolean":
                        Object boolObj = getMethod.invoke(object2Entity);
                        if (boolObj != null) {
                            Boolean bool = (Boolean) boolObj;
                            Method setMethod = entity.getClass()
                                    .getDeclaredMethod("set" + name, new Class[]{Boolean.class});
                            setMethod.invoke(entity, new Object[] {bool});
                        }
                        break;
                    case "class java.lang.Date":
                        Object dateObj = getMethod.invoke(object2Entity);
                        if (dateObj != null) {
                            Date date = (Date) dateObj;
                            Method setMethod = entity.getClass()
                                    .getDeclaredMethod("set" + name, new Class[]{Date.class});
                            setMethod.invoke(entity, new Object[] {date});
                        }
                        break;
                    default:
                        break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return entity;
    }
}
